﻿

﻿using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2;
using Inovout.Account.Web.Models;
using Inovout.Web.Mvc;
using System.Net;
using System.Web;
using System.Linq;
using System.Web.Mvc;
using Inovout.Web.Security.OAuth2;
using Inovout.Cloud.Applications.Security.Models;
using Inovout.Cloud.Applications.Security.Services;
using Inovout.Cloud.Models;
using Inovout.Cloud.Services;
using System.Collections.Generic;
using Inovout.Security;
using Inovout.Services;
using DotNetOpenAuth.OAuth2.Messages;
using System;

namespace Inovout.Account.Web.Controllers
{
    public class OAuthController : BaseController
    {
        //todo:为何不用DOTNETOPENAUTH自带的AuthorizationServerHost？？？
        protected readonly AuthorizationServer authorizationServer
            = new AuthorizationServer(new OAuth2AuthorizationServer());

        protected OutgoingWebResponse GetTokenResponse()
        {
            var respose = this.authorizationServer.HandleTokenRequest(this.Request);
            return respose;
        }
        /// <summary>
        /// The OAuth 2.0 token endpoint.
        /// </summary>
        /// <returns>The response to the Client.</returns>
        [AllowAnonymous]
        public virtual ActionResult Token()
        {
            //var requestMessage =
            //    this.authorizationServer.Channel.ReadFromRequest<DotNetOpenAuth.OAuth2.Messages.AccessTokenRequestBase>(this.Request);
            //var messageReceivingEndpoint = new MessageReceivingEndpoint(this.Request.Url, HttpDeliveryMethods.PostRequest);
            //var fields = HttpUtility.ParseQueryString(this.Request.Url.Fragment.Substring(1)).ToDictionary();
            return this.GetTokenResponse().AsActionResultMvc5();
        }

        /// <summary>
        /// Prompts the user to authorize a client to access the user's private data.
        /// </summary>
        /// <returns>The browser HTML response that prompts the user to authorize the client.</returns>
        [Authorize, AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
        [HttpHeader("x-frame-options", "SAMEORIGIN")] // mitigates clickjacking
        public virtual ActionResult Authorize()
        {
            var pendingRequest = this.authorizationServer.ReadAuthorizationRequest();
            if (pendingRequest == null)
            {
                throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
            } 

            //var requestingClient = MvcApplication.DataContext.Clients.First(c => c.ClientIdentifier == pendingRequest.ClientIdentifier);

            Inovout.Cloud.Applications.Models.Application application = ComponentRegistry.Resolve<OAuthCredentialService>()
                 .FindByClientId(pendingRequest.ClientIdentifier).ApplicationUser.Application;
            Project project = ComponentRegistry.Resolve<IProjectService>().FindByApplication(application);
            //// Consider auto-approving if safe to do so.
            //这步是自动授权，先暂时不实现。
            if (((OAuth2AuthorizationServer)this.authorizationServer.AuthorizationServerServices).CanBeAutoApproved(pendingRequest))
            {
                Uri callback = pendingRequest.Callback;
                if (callback.Equals(new Uri("urn:ietf:wg:oauth:2.0:oob")))
                {
                    callback = new Uri(new Uri(this.Request.Url, Url.Action("AuthorizeSuccess")).AbsoluteUri);
                }
                var approval = this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, HttpContext.User.Identity.Name, callback: callback);             
                return this.authorizationServer.Channel.PrepareResponse(approval).AsActionResultMvc5();
            }

            List<string> scopeDescriptiion = new List<string>();
            IRepositoryService<Privilege> privilegeService = ComponentRegistry.Resolve<IRepositoryService<Privilege>>();
            foreach (var privilegeScope in pendingRequest.Scope)
            {
               var resource= privilegeService.Query().Single(p => p.Identifier.Equals(privilegeScope)).Resource;
               scopeDescriptiion.Add(resource.Title);
            }
            var model = new AuthorizeInfo
            {
                ClientApp = project.Title,
                Scope = scopeDescriptiion.Distinct(),
                AuthorizationRequest = pendingRequest,
            };

            return View(model);
        }

        public ActionResult AuthorizeSuccess()
        {
            //ViewBag.Token = this.Request.Url.AbsoluteUri.ToString().Split('&')[1].Split('=')[1];
            //ViewBag.Token = this.Request.Url.AbsoluteUri.ToString();
            return View();
        }

        /// <summary>
        /// Processes the user's response as to whether to authorize a Client to access his/her private data.
        /// </summary>
        /// <param name="isApproved">if set to <c>true</c>, the user has authorized the Client; <c>false</c> otherwise.</param>
        /// <returns>HTML response that redirects the browser to the Client.</returns>
        [Authorize, HttpPost]
        public ActionResult AuthorizeResponse(bool isApproved)
        {
            var pendingRequest = this.authorizationServer.ReadAuthorizationRequest();
            if (pendingRequest == null)
            {
                throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
            }
           
            IDirectedProtocolMessage response;
            if (isApproved)
            {
                // The authorization we file in our database lasts until the user explicitly revokes it.
                // You can cause the authorization to expire by setting the ExpirationDateUTC
                // property in the below created ClientAuthorization.
                //var client = new ClientDescription(pendingRequest.ClientIdentifier);
                //client.ClientAuthorizations.Add(
                //    new ClientAuthorization
                //    {
                //        Scope = OAuthUtilities.JoinScopes(pendingRequest.Scope),
                //        User = MvcApplication.LoggedInUser,
                //        CreatedOnUtc = DateTime.UtcNow,
                //    });
                //MvcApplication.DataContext.SubmitChanges(); // submit now so that this new row can be retrieved later in this same HTTP request

                //todo 将用户已经授权的信息保存起来。

                //In this simple sample, the user either agrees to the entire scope requested by the client or none of it.  
                //But in a real app, you could grant a reduced scope of access to the client by passing a scope parameter to this method.
                response = this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, User.Identity.Name);
            }
            else
            {
                response =this.authorizationServer.PrepareRejectAuthorizationRequest(pendingRequest);
                ((EndUserAuthorizationFailedResponse)response).Error = "access_denied";
            }

            return this.authorizationServer.Channel.PrepareResponse(response).AsActionResultMvc5();
        }
    }

}

